概述
DiffUtil是recyclerview support library v7 24.2.0版本中新增的类,根据Google官方文档的介绍,DiffUtil的作用是比较两个数据列表并能计算出一系列将旧数据表转换成新数据表的操作。这个概念比较抽象,换一种方式理解,DiffUtil是一个工具类,当你的RecyclerView需要更新数据时,将新旧数据集传给它,它就能快速告知adapter有哪些数据需要更新。
那么相比直接调用adapter.notifyDataSetChange()方法,使用DiffUtil有什么优势呢?它能在收到数据集后,提高UI更新的效率,而且你也不需要自己对新老数据集进行比较了。
顾名思义,凡是数据集的比较DiffUtil都能做,所以用处并不止于更新RecyclerView。DiffUtil也提供了回调让你可以进行其他操作。本文只介绍使用DiffUtil更新RecyclerView。
DiffUtil简介
在使用DiffUtil前我们先简单看看DiffUtil的特性。DiffUtil使用Eugene W. Myers的Difference算法来计算出将一个数据集转换为另一个的最小更新量,也就是用最简单的方式将一个数据集转换为另一个。除此之外,DiffUtil还可以识别一项数据在数据集中的移动。Eugene的算法对控件进行了优化,在查找两个数据集间最少加减操作时的空间复杂度为O(N),时间复杂度为O(N+D^2)
。而如果添加了对数据条目移动的识别,复杂度就会提高到O(N^2)
。所以如果数据集中数据不存在移位情况,你可以关闭移动识别功能来提高性能。当数据集较大时,你应该在后台线程计算数据集的更新。
如何使用
DiffUtil类
- DiffUtil.Callback:这是最核心的类,你可以将它理解成比较新老数据集时的规则。
- DiffUtil:通过静态方法
DiffUtil.calculateDiff(DiffUtil.Callback)
来计算数据集的更新。 - DiffResult:是DiffUtil的计算结果对象,通过
DiffResult.dispatchUpdatesTo(RecyclerView.Adapter)
来进行更新。
代码模式为
|
|
dispatchUpdatesTo()
方法它会自动计算新老数据集的差异,并根据差异情况,自动调用以下四个方法
|
|
DiffUtil.Callback抽象类
|
|
DiffUtil步骤
- 自定义类继承DiffUtil.Callback,通过重写特定方法给出数据比较逻辑。
- 调用
DiffUtil.calculateDiff(DiffUtil.Callback callback,boolean detectMove)
来计算更新,得到DiffResult对象。第二个参数可省,意为是否探测数据的移动,是否关闭需要根据数据集情况来权衡。当数据集很大时,此操作可能耗时较长,需要异步计算。 - 在UI线程中调用DiffResult.dispatchUpdatesTo(RecyclerView.Adapter),而后Adapter的onBindViewHolder(RecyclerView.ViewHolder holder, int position, Listpayloads)。注意这个方法比必须覆盖的onBindViewHolder(RecyclerView.ViewHolder holder, int position)方法多一个参数payloads,而里面存储了数据的更新。
示例
初始化RecyclerView
- 新建一个Bean为Item:
|
|
- 新建Adapter
|
|
- 初始化ReyclerView
|
|
初始化RecyclerView后效果为:
实现DiffUtil.Callback
新建类继承DiffUtil.Callback
|
|
使用DiffUtil
下面通过两种不同的改变RecyclerView条目来介绍DiffUtil的使用。
- 增加或删除条目
这种情况下,数据集的大小改变,反映在RecyclerView的效果就是增加或者删除条目
|
|
增加条目后的RecyclerView的效果为:
- 更新具体的条目
这种情况下数据集大小不改变,改变数据集中条目的内容,反映在RecyclerView的效果就是更新具体的条目,这回调用Callback中的getChangePayload方法,而Adapter必须要实现public void onBindViewHolder(DiffItemHolder holder, int position, List<Object> payloads)
方法。
|
|
更新条目后的RecyclerView效果为:
由图可知,第四个位置的条目显示变为zhang。
结语
DiffUtil可用于高效进行RecyclerView的数据更新,但DiffUtil本身的作用是计算数据集的最小更新。DiffUtil有强大的算法支撑,可以利用DiffUtil完成许多其他功能。
示例代码
参考文章: